﻿using System;
using System.Collections.Generic;
using System.Drawing;
using System.IO;
using System.Net;
using System.Security.Cryptography;
using System.Text;
using MemoryDuosControl;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;

namespace MemoryDuosControl
{
    public class Usuario
    {
        public int ID { get; set; }

        public String Email { get; set; }

        public String Nick { get; set; }

        public String Pass { get; set; }

        public String Nombre { get; set; }

        public String Apellidos { get; set; }

        public String Sexo { get; set; }

        public String FechaNacimiento { get; set; }

        public String Nacionalidad { get; set; }

        public int Monedas { get; set; }

        [JsonIgnore]
        private Bitmap Foto { get; set; }

        [JsonIgnore]
        public Image ImagenPerfil { get; set; }

        public String SessionID { get; set; }

        public Usuario()
        {
        }

        public Usuario(string email, string nick, string password)
        {
            Email = email;
            Nick = nick;
            Pass = password;
            CalcularHash();
            System.Net.ServicePointManager.Expect100Continue = false;
        }

        public void CalcularHash()
        {
            Pass = SHA256Encrypt(Pass);
        }

        public string SHA256Encrypt(string input)
        {
            SHA256CryptoServiceProvider provider = new SHA256CryptoServiceProvider();

            byte[] inputBytes = Encoding.UTF8.GetBytes(input);
            byte[] hashedBytes = provider.ComputeHash(inputBytes);

            StringBuilder output = new StringBuilder();

            for (int i = 0; i < hashedBytes.Length; i++)
                output.Append(hashedBytes[i].ToString("x2").ToLower());

            return output.ToString();
        }

        public bool InsertarEnBD()
        {
            string address = string.Format(Conexion.UrlPruebas + "registro.php");
            String text;
            String json = JsonConvert.SerializeObject(this);
            using (CookieAwareWebClient client = new CookieAwareWebClient())
            {
                text = client.UploadString(address, "POST", json);
            }
            try
            {
                EstadoPeticion parseado = JsonConvert.DeserializeObject<EstadoPeticion>(text);
                if (!parseado.Estado)
                {
                    throw new Exception(parseado.Error);
                    return false;
                }
                return true;
            }
            catch (JsonSerializationException exc)
            {
                throw new Exception("Hubo un error al procesar la información del servidor al registrarse: " + text, exc);
            }
        }

        public bool InsertarDatosActualizados()
        {
            string address = string.Format(Conexion.UrlPruebas + "registro.php");
            String text;
            String json = JsonConvert.SerializeObject(this);
            using (CookieAwareWebClient client = new CookieAwareWebClient(this.SessionID))
            {
                text = client.UploadString(address, "POST", json);
            }
            try
            {
                EstadoPeticion parseado = JsonConvert.DeserializeObject<EstadoPeticion>(text);
                if (!parseado.Estado)
                {
                    throw new Exception("Hubo un error al actualizar los datos de usuario en el servidor: " + parseado.Error);
                    return false;
                }
                return true;
            }
            catch (JsonSerializationException exc)
            {
                throw new Exception("Hubo un error al procesar la información del servidor: " + text, exc);
            }
        }

        public bool Login()
        {
            string address = string.Format(Conexion.UrlPruebas + "login.php?email={0}&pass={1}", this.Email, this.Pass);
            String text;
            var request = WebRequest.Create(address);

            using (var webClient = new WebClient())
            {
                text = webClient.DownloadString(address);
                this.SessionID = webClient.ResponseHeaders[HttpResponseHeader.SetCookie];
            }

            EstadoPeticion parseado = JsonConvert.DeserializeObject<EstadoPeticion>(text);
            if (!parseado.Estado)
            {
                throw new Exception(parseado.Error);
            }
            return true;
        }

        public static Usuario ObtenerUsuarioPorID(int id)
        {
            string address = string.Format(Conexion.UrlPruebas + "usuarios.php?id={0}", id);
            String text;
            using (CookieAwareWebClient client = new CookieAwareWebClient())
            {
                text = client.DownloadString(address);
            }
            return JsonConvert.DeserializeObject<Usuario>(text);
        }

        public Usuario ActualizarDatosUsuario()
        {
            string address = string.Format(Conexion.UrlPruebas + "usuarios.php");
            String text;
            using (CookieAwareWebClient client = new CookieAwareWebClient(this.SessionID))
            {
                text = client.DownloadString(address);
            }
            Usuario aDevolver = JsonConvert.DeserializeObject<Usuario>(text, new ImageConverter());
            aDevolver.SessionID = this.SessionID;
            byte[] bytesDevolver;
            using (var cliente = new CookieAwareWebClient(this.SessionID))
            {
                bytesDevolver = cliente.DownloadData(Conexion.UrlPruebas + "perfil.php");
                var str = System.Text.Encoding.Default.GetString(bytesDevolver);
            }
            if (bytesDevolver.Length > 0)
                aDevolver.ImagenPerfil = byteArrayToImage(bytesDevolver);
            return aDevolver;
        }

        public String Serializar()
        {
            return JsonConvert.SerializeObject(this, new ImageConverter());
        }

        public static Image ObtenerImagenNick(string nick)
        {
            byte[] bytesDevolver;
            using (var cliente = new CookieAwareWebClient())
            {
                bytesDevolver = cliente.DownloadData(Conexion.UrlPruebas + String.Format("perfil.php?nick={0}", nick));
                var str = System.Text.Encoding.Default.GetString(bytesDevolver);
            }
            if (bytesDevolver.Length > 0)
                return byteArrayToImage(bytesDevolver);
            return null;
        }

        public void SubirImagen(string ubicacion)
        {
            string address = string.Format(Conexion.UrlPruebas + "perfil.php?email={0}", this.Email);
            byte[] text;
            ImagenPerfil = Image.FromFile(ubicacion);
            using (var cliente = new CookieAwareWebClient())
            {
                text = cliente.UploadFile(address, ubicacion);
                var str = System.Text.Encoding.Default.GetString(text);
                Console.Write(str);
            }
        }

        public void PedirRecoverID()
        {
            string address = string.Format(Conexion.UrlPruebas + "recover.php?email={0}", this.Email);
            String text;
            var request = WebRequest.Create(address);

            using (var webClient = new WebClient())
            {
                text = webClient.DownloadString(address);
            }
        }

        public bool RestablecerContraseña(string recoverID)
        {
            string address = string.Format(Conexion.UrlPruebas + "recover.php?email={0}&pass={1}&recoverid={2}", this.Email, this.Pass, recoverID);
            String text;
            var request = WebRequest.Create(address);

            using (var webClient = new WebClient())
            {
                text = webClient.DownloadString(address);
            }
            EstadoPeticion parseado = JsonConvert.DeserializeObject<EstadoPeticion>(text);
            if (!parseado.Estado)
            {
                throw new Exception(parseado.Error);
            }
            return true;
        }

        private List<Partida> ObtenerPartidaTipo(String tipo)
        {
            string address = string.Format(Conexion.UrlPruebas + "partidas.php?tipo={0}", tipo);
            String text;
            var request = WebRequest.Create(address);

            using (CookieAwareWebClient client = new CookieAwareWebClient(this.SessionID))
            {
                text = client.DownloadString(address);
            }
            if (text.Equals("[{}]")) return null;
            try
            {
                List<Partida> lista = JsonConvert.DeserializeObject<List<Partida>>(text);
                return lista;
            }
            catch (JsonSerializationException exc)
            {
                throw new Exception("Hubo un error al parsear la lista de partidas " + tipo + " . El texto obtenido del servidor fue: " + text, exc);
            }
        }

        public Partida ObtenerPartidaID(int id)
        {
            string address = string.Format(Conexion.UrlPruebas + "partidas.php?id={0}", id);
            String text;
            var request = WebRequest.Create(address);
            using (CookieAwareWebClient client = new CookieAwareWebClient(this.SessionID))
            {
                text = client.DownloadString(address);
            }
            if (text.Equals("{}")) return null;
            try
            {
                return JsonConvert.DeserializeObject<Partida>(text);
            }
            catch (JsonSerializationException exc)
            {
                throw new Exception("Hubo un error al obtener la partida del servidor, esto es lo que respondió: " + text);
            }
        }

        public List<Partida> ObtenerPartidasEnJuego()
        {
            return this.ObtenerPartidaTipo("tetoca");
        }

        public List<Partida> ObtenerPartidasPendientes()
        {
            return this.ObtenerPartidaTipo("pendientes");
        }

        public List<Partida> ObtenerPartidasFinalizadas()
        {
            return this.ObtenerPartidaTipo("finalizadas");
        }

        public List<Partida> ObtenerPartidasRechazadas()
        {
            return this.ObtenerPartidaTipo("canceladas");
        }

        private EstadoPeticion ModificarPartida(string accion, int id)
        {
            string address = string.Format(Conexion.UrlPruebas + "partidas.php?{0}={1}", accion, id);
            String text;
            var request = WebRequest.Create(address);

            using (CookieAwareWebClient client = new CookieAwareWebClient(this.SessionID))
            {
                text = client.DownloadString(address);
            }

            try
            {
                return JsonConvert.DeserializeObject<EstadoPeticion>(text);
            }
            catch (JsonSerializationException exce)
            {
                throw new Exception("Hubo un error al enviar la modificación de partida al servidor, el servidor devolvió lo siguiente: " + text);
            }
        }

        public EstadoPeticion RechazarPartida(int id)
        {
            return ModificarPartida("rechazar", id);
        }

        public EstadoPeticion AbandonarPartida(int id)
        {
            return ModificarPartida("abandonar", id);
        }

        public EstadoTablero ObtenerTablero(int id)
        {
            string address = string.Format(Conexion.UrlPruebas + "partidas.php?continuar={0}", id);
            String text;
            var request = WebRequest.Create(address);

            using (CookieAwareWebClient client = new CookieAwareWebClient(this.SessionID))
            {
                text = client.DownloadString(address);
            }

            try
            {
                return JsonConvert.DeserializeObject<EstadoTablero>(text);
            }
            catch (JsonSerializationException exc)
            {
                throw new Exception("Hubo un error al obtener el tablero del servidor. El servidor devolvió los siguientes datos. " + text);
            }
        }

        /*public void Aceptar(int id)
        {
            string address = string.Format(Conexion.UrlPruebas + "partidas.php?Aceptar={0}", id);
        }*/

        public EstadoPeticion EnviarMovimiento(string json)
        {
            string address = string.Format(Conexion.UrlPruebas + "jugada.php?movimiento=jugada");
            String text;
            using (CookieAwareWebClient client = new CookieAwareWebClient(this.SessionID))
            {
                text = client.UploadString(address, "POST", json);
            }
            EstadoPeticion parseado = null;
            try { parseado = JsonConvert.DeserializeObject<EstadoPeticion>(text); }
            catch (Exception e)
            {
                throw new Exception("Error al parsear el estado proviniente del servidor al enviar el movimiento. El servidor devolvió: " + text);
            }

            if (!parseado.Estado)
            {
                throw new Exception("Error al enviar el movimiento de la partida: " + parseado.Error + " Datos Enviados:\n" + json);
            }
            return parseado;
        }

        public EstadoPeticion GuardarEstado(string json)
        {
            string address = string.Format(Conexion.UrlPruebas + "jugada.php?movimiento=iniciar");
            String text;
            using (CookieAwareWebClient client = new CookieAwareWebClient(this.SessionID))
            {
                text = client.UploadString(address, "POST", json);
            }
            EstadoPeticion parseado = null;
            try { parseado = JsonConvert.DeserializeObject<EstadoPeticion>(text); }
            catch (Exception e)
            {
                throw new Exception("Error al parsear el estado proviniente del servidor al enviar el movimiento. El servidor devolvió: " + text);
            }

            if (!parseado.Estado)
            {
                throw new Exception("Error al enviar el movimiento de la partida: " + parseado.Error + " Datos Enviados:\n" + json);
            }
            return parseado;
        }

        public void UtilizarPowerUP(int idPartida)
        {
            string address = string.Format(Conexion.UrlPruebas + "jugada.php?movimiento=powerup");
            String text;
            JObject o = new JObject();
            o["ID"] = idPartida;
            using (CookieAwareWebClient client = new CookieAwareWebClient(this.SessionID))
            {
                text = client.UploadString(address, "POST", o.ToString());
            }
        }

        public static EstadoPeticion VerificarEmail(string email)
        {
            string address = string.Format(Conexion.UrlPruebas + String.Format("verificarEmail.php?email={0}", email));
            String text;
            using (CookieAwareWebClient client = new CookieAwareWebClient())
            {
                text = client.DownloadString(address);
            }
            EstadoPeticion parseado = JsonConvert.DeserializeObject<EstadoPeticion>(text);
            return parseado;
        }

        public EstadoPeticion FinalizarPartida(string json, int IDJugadorGana, int PuntuacionGana)
        {
            string address = string.Format(Conexion.UrlPruebas + String.Format("jugada.php?finalizar={0}&puntuacion={1}", IDJugadorGana, PuntuacionGana));
            String text;
            using (CookieAwareWebClient client = new CookieAwareWebClient(this.SessionID))
            {
                text = client.UploadString(address, "POST", json);
            } EstadoPeticion parseado = null;
            try { parseado = JsonConvert.DeserializeObject<EstadoPeticion>(text); }
            catch (Exception e)
            {
                throw new Exception("Error al parsear el estado proviniente del servidor al finalizar. El servidor devolvió: " + text);
            }
            if (!parseado.Estado)
            {
                throw new Exception("Hubo un error al finalizar la partida: " + parseado.Error);
            }
            return parseado;
        }

        private static Image byteArrayToImage(byte[] byteArrayIn)
        {
            MemoryStream ms = new MemoryStream(byteArrayIn);
            Image returnImage = Image.FromStream(ms);
            return returnImage;
        }

        private String ToUTF8(string text)
        {
            var utf8 = Encoding.Unicode;
            byte[] utfBytes = utf8.GetBytes(text);

            return utf8.GetString(utfBytes);
        }

        public void ActualizarMonedas(int monedas)
        {
            string address = Conexion.UrlPruebas + String.Format("monedas.php?Monedas={0}&Nick={1}", monedas, Nick);
            using (var webClient = new WebClient())
            {
                webClient.DownloadString(address);
            }
            Monedas = Monedas + monedas;
        }
    }
}

public class BoolConverter : JsonConverter
{
    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        writer.WriteValue(((bool)value) ? 1 : 0);
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        return reader.Value.ToString() == "1";
    }

    public override bool CanConvert(Type objectType)
    {
        return objectType == typeof(bool);
    }
}

public class ImageConverter : Newtonsoft.Json.JsonConverter
{
    public override bool CanConvert(Type objectType)
    {
        return objectType == typeof(Bitmap);
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        var m = new MemoryStream(Convert.FromBase64String((string)reader.Value));
        return (Bitmap)Bitmap.FromStream(m);
    }

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        Bitmap bmp = (Bitmap)value;
        MemoryStream m = new MemoryStream();
        bmp.Save(m, System.Drawing.Imaging.ImageFormat.Png);

        writer.WriteValue(Convert.ToBase64String(m.ToArray()));
    }
}